package com.seed.core.query;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import com.seed.core.column.Column;
import com.seed.core.enumeration.JoinType;
import com.seed.core.enumeration.PropertyType;
import com.seed.core.enumeration.SqlOp;
import com.seed.core.pojo.Join;
import com.seed.core.pojo.seed.*;
import com.seed.core.query.pojo.Declared;
import com.seed.core.query.pojo.Searched;
import com.seed.core.util.ConditionUtil;
import com.seed.util.StrKit;
import com.seed.util.SeedKit;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class SeedInCondition {
    private final Seed selfSeed;
    private final String basePath;
    private final String cross;
    private final HasManys hasMany;
    private final Seed targetSeed;
    private Searched searched; // 条件声明
    private Declared declared; // 关键字声明
    private String keyword;
    private List<Map<String, Searched>> dataPermied;

    private final Map<String, Join> joins = new LinkedHashMap<>(); // 表关联对象
    private final List<QueryCondition> wheres = new ArrayList<>(); // 条件对象
    private final List<QueryCondition> keywords = new ArrayList<>(); // 关键字条件对象

    public SeedInCondition(Seed selfSeed, String basePath, String cross, HasManys hasMany, Seed targetSeed, Searched searched) {
        this.selfSeed = selfSeed;
        this.basePath = basePath;
        this.cross = cross;
        this.hasMany = hasMany;
        this.targetSeed = targetSeed;
        this.searched = searched;
    }

    public SeedInCondition(Seed selfSeed, String basePath, String cross, HasManys hasMany, Seed targetSeed, String keyword, Declared declared) {
        this.selfSeed = selfSeed;
        this.basePath = basePath;
        this.cross = cross;
        this.hasMany = hasMany;
        this.targetSeed = targetSeed;
        this.keyword = keyword;
        this.declared = declared;
    }

    private void buildWhere(String basePath, Seed seed, Map<String, Searched> searcheds) {
        String selfTableAlias = StrKit.selfTableAlias(basePath);
        QueryTable selfTable = new QueryTable(seed.getTableName()).as(selfTableAlias);
        for(Map.Entry<String, Searched> entry: searcheds.entrySet()) {
            String[] items = StrKit.cutInTwo(StrKit.cutInTwo(entry.getKey(), '=', false)[0], '_', false);
            String crossOrColumn = items[0];
            String op = items[1];
            String finalPath = StrKit.concat(basePath, crossOrColumn);
            Searched value = entry.getValue();
            PropertyType propertyType = value.getType();
            switch (propertyType) {
                case none -> {
                    Column nameColumn = SeedKit.getNameColumn(selfSeed);
                    String val = value.getValue();
                    wheres.add(ConditionUtil.condition(selfTable, nameColumn.getName(), StrUtil.isBlank(op) ? nameColumn.getDefaultOp().name() : op, val));
                }
                case asterisk -> {
                    Map<String, Column> columns = selfSeed.getColumns();
                    for (Column column: columns.values()) {
                        String columName = column.getName();
                        String val = value.getValue();
                        wheres.add(ConditionUtil.condition(selfTable, columName, column.getDefaultOp().name(), val));
                    }
                }
                case column -> {
                    String val = value.getValue();
                    Column column = (Column) value.getProperty();
                    op = StrUtil.isBlank(op) ? column.getDefaultOp().name() : op;
                    wheres.add(ConditionUtil.condition(selfTable, crossOrColumn, op, val));
                }
                case hasOne, belongsTo, hasMany -> {
                    Relation relation = (Relation) value.getProperty();
                    Seed targetSeed = relation.targetSeed();
                    String targetTableAlias = StrKit.targetTableAlias(basePath, crossOrColumn);
                    QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
                    QueryCondition on = relation.getQueryCondition(basePath, crossOrColumn);
                    joins.putIfAbsent(finalPath, new Join(targetTable, on));
                    buildWhere(finalPath, targetSeed, value);
                }
                case habtm -> {
//                    Relation relation = (Relation) value.getProperty();
//                    Seed targetSeed = relation.targetSeed();
//                    String targetTableAlias = StrKit.targetTableAlias(basePath, crossOrColumn);
//                    QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//
//                    Habtms habtm = (Habtms) relation;
//                    String throughAlias = StrKit.throughAlias(basePath, crossOrColumn);
//                    QueryTable throughTable = new QueryTable(habtm.throughTableName()).as(throughAlias);
//                    QueryCondition throughOn = habtm.getThroughQueryCondition(selfTableAlias, throughAlias);
//                    joins.putIfAbsent(throughAlias, new Join(throughTable, throughOn, JoinType.inner));
//
//                    QueryCondition on = habtm.getQueryCondition(selfTableAlias, throughAlias, targetTableAlias);
//                    joins.putIfAbsent(finalPath, new Join(targetTable, on));
//                    buildWhere(finalPath, targetSeed, value);
                }
            }
        }
    }

    private void buildKeywords(String basePath, Seed seed, Map<String, Declared> declared) {
        String selfTableAlias = StrKit.selfTableAlias(basePath);
        QueryTable selfTable = new QueryTable(seed.getTableName()).as(selfTableAlias);
        for(Map.Entry<String, Declared> entry: declared.entrySet()) {
            String crossOrColumn = entry.getKey();
            String finalPath = StrKit.concat(basePath, crossOrColumn);
            Declared value = entry.getValue();
            PropertyType propertyType = value.getType();
            switch (propertyType) {
                case none -> {
                    Column nameColumn = SeedKit.getNameColumn(selfSeed);
//                    SqlOp op = SeedKit.op(selfSeed, nameColumn);
                    keywords.add(ConditionUtil.condition(selfTable, nameColumn.getName(), nameColumn.getDefaultOp().name(), keyword));
                }
                case asterisk -> {
                    Map<String, Column> columns = selfSeed.getColumns();
                    for (Column column: columns.values()) {
                        String columName = column.getName();
//                        SqlOp op = SeedKit.op(selfSeed, columName);
                        keywords.add(ConditionUtil.condition(selfTable, columName, column.getDefaultOp().name(), keyword));
                    }
                }
                case column -> {
//                    SqlOp op = SeedKit.op(selfSeed, crossOrColumn);
                    Column column = (Column) value.getProperty();
                    keywords.add(ConditionUtil.condition(selfTable, crossOrColumn, column.getDefaultOp().name(), keyword));
                }
                case belongsTo, hasOne, hasMany -> {
                    Relation relation = (Relation) value.getProperty();
                    Seed targetSeed = relation.targetSeed();
                    String targetTableAlias = StrUtil.toCamelCase(finalPath);
                    QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
                    QueryCondition on = relation.getQueryCondition(basePath, crossOrColumn);
                    joins.putIfAbsent(finalPath, new Join(targetTable, on));
                }
                case habtm -> {
//                    Relation relation = (Relation) value.getProperty();
//                    Seed targetSeed = relation.targetSeed();
//                    String targetTableAlias = StrUtil.toCamelCase(finalPath);
//                    QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//                    Habtms habtm = (Habtms) relation;
//                    String throughSelfColumn = habtm.throughSelfColumn();
//                    String throughTargetColumn = habtm.throughTargetColumn();
//                    String throughAlias = StrKit.throughAlias(throughSelfColumn, throughTargetColumn);
//                    QueryTable throughTable = new QueryTable(habtm.throughTableName()).as(throughAlias);
//                    QueryCondition throughOn = habtm.getThroughQueryCondition(selfTableAlias, throughAlias);
//                    joins.putIfAbsent(throughAlias, new Join(throughTable, throughOn, JoinType.inner));
//
//                    QueryCondition on = habtm.getQueryCondition(selfTableAlias, throughAlias, targetTableAlias);
//                    joins.putIfAbsent(finalPath, new Join(targetTable, on));
                }
            }

            buildKeywords(finalPath, targetSeed, value);
        }
    }

    public QueryCondition getQueryCondition() {
        if((StrUtil.isNotBlank(keyword) && declared != null) || searched != null) joinToTarget();

        String finalPath = StrKit.concat(basePath, cross);
        if(StrUtil.isNotBlank(keyword) && declared != null) {
            buildKeywords(finalPath, targetSeed, declared);
        }

        if(searched != null) {
            buildWhere(finalPath, targetSeed, searched);
        }

        String selfTableAlias = StrKit.selfTableAlias(basePath);
        QueryTable selfTable = new QueryTable(selfSeed.getTableName()).as(selfTableAlias);
        QueryWrapper qw = QueryWrapper.create().select(new QueryColumn(selfTable, "id")).from(selfTable);
        for(Join join: joins.values()) {
            if(join.getJoinType().equals(JoinType.inner)) {
                qw.innerJoin(join.getTable()).on(join.getOn());
            } else if (join.getJoinType().equals(JoinType.left)){
                qw.leftJoin(join.getTable()).on(join.getOn());
            } else {
                // 右连接暂时没用到
            }
        }
        for(QueryCondition condition: wheres) qw.and(condition);

        qw.and(queryWrapper -> {
            for(QueryCondition condition: keywords) queryWrapper.or(condition);
        });

        return new QueryColumn(selfTable, "id").in(qw);
    }

    private void joinToTarget() {
//        String selfTableAlias = StrKit.selfTableAlias(basePath);
//        String targetTableAlias = StrKit.targetTableAlias(basePath, cross);
//        String finalPath = StrKit.concat(basePath, cross);
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        if(hasMany instanceof Habtms habtm) {
//            String throughAlias = StrKit.throughAlias(basePath, cross);
//            QueryTable throughTable = new QueryTable(habtm.throughTableName()).as(throughAlias);
//            QueryCondition throughOn = habtm.getThroughQueryCondition(selfTableAlias, throughAlias);
//            joins.putIfAbsent(throughAlias, new Join(throughTable, throughOn, JoinType.inner));
//
//            QueryCondition on = habtm.getQueryCondition(selfTableAlias, throughAlias, targetTableAlias);
//            joins.putIfAbsent(finalPath, new Join(targetTable, on));
//        } else {
//            QueryCondition on = hasMany.getQueryCondition(basePath, cross);
//            joins.putIfAbsent(finalPath, new Join(targetTable, on));
//        }
    }

    public Declared getDeclared() {
        return declared;
    }

    public void setDeclared(Declared declared) {
        this.declared = declared;
    }

    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    public List<Map<String, Searched>> getDataPermied() {
        return dataPermied;
    }

    public void setDataPermied(List<Map<String, Searched>> dataPermied) {
        this.dataPermied = dataPermied;
    }
}
